﻿/*
Copyright (c) 2010 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows.Forms;
using BrightIdeasSoftware;
using TeachMe.Application.Configuration;
using TeachMe.Application.Controls;
using TeachMe.Application.Data;
using TeachMe.Application.Events;
using TeachMe.Application.Exceptions;
using TeachMe.Application.Properties;

namespace TeachMe.Application.Forms
{
    public partial class DictionariesManagerForm : PseudoModalForm
    {
        private readonly Root _root;
        private readonly List<EditedDictionaryDescriptor> _editedDicts = new List<EditedDictionaryDescriptor>();

        public DictionariesManagerForm(Root root)
        {
            _root = root;
            _root.UserSettings.CheckOut();
            _root.MachineSettings.CheckOut();

            InitializeComponent();
            InitializeDictsTable();
            InitializePriorityChanger();

            LoadDicts();

            WindowProportions.Lock(this, _root.MachineSettings);
            ListViewColumnsProportions.Lock(this, lvDictionaries, _root.MachineSettings);
        }

        private void SaveDicts()
        {
            foreach (EditedDictionaryDescriptor descriptor in _editedDicts)
            {
                switch (descriptor.State)
                {
                    case EditedItemState.New:
                    case EditedItemState.Normal:
                        if (descriptor.IsModified())
                        {
                            descriptor.Dictionary.UpdateUserSettings(descriptor.Enabled, descriptor.EntryTextDirection, descriptor.Priority);
                        }
                        break;
                    case EditedItemState.Deleted:
                        _root.RemoveDictionary(descriptor.Dictionary);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void LoadDicts()
        {
            _editedDicts.Clear();

            foreach (Dictionary dictionary in _root.Dictionaries)
            {
                _editedDicts.Add(EditedDictionaryDescriptor.Create(dictionary));
            }

            UpdateDicts();
        }

        private void InitializeDictsTable()
        {
            ObjectListView.EditorRegistry.Register(typeof(EntryTextDirection), typeof(DirectionComboBox));
            ObjectListView.EditorRegistry.Register(typeof(Priority), typeof(PriorityComboBox));

            // let height be like combo
            lvDictionaries.RowHeight = new ComboBox().Height;

            InitPriorityColumn();
            InitDirectionColumn();
        }

        private void InitDirectionColumn()
        {
            colDirection.AspectType = typeof(EntryTextDirection);

            colDirection.AspectGetter += delegate(object rowObject)
            {
                EditedDictionaryDescriptor descriptor = rowObject as EditedDictionaryDescriptor;
                if (descriptor == null)
                {
                    throw new InvalidOperationException();
                }

                string from = Languages.GetByCode(descriptor.Dictionary.Entries.From) != null ? Languages.GetByCode(descriptor.Dictionary.Entries.From).Localized: descriptor.Dictionary.Entries.From;
                string to = Languages.GetByCode(descriptor.Dictionary.Entries.To) != null ? Languages.GetByCode(descriptor.Dictionary.Entries.To).Localized : descriptor.Dictionary.Entries.To;

                switch (descriptor.EntryTextDirection)
                {
                    case EntryTextDirection.FromTo:
                        return String.Format(Resources.OneDirection, from, to);
                    case EntryTextDirection.ToFrom:
                        return String.Format(Resources.OneDirection, to, from);
                    case EntryTextDirection.Both:
                        return String.Format(Resources.BothDirections, from , to);
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            };

            colDirection.AspectPutter += delegate
            {
                // ignored
            };
        }

        private void InitPriorityColumn()
        {
            colPriority.AspectType = typeof(Priority);

            colPriority.AspectGetter += delegate(object rowObject)
            {
                EditedDictionaryDescriptor descriptor = rowObject as EditedDictionaryDescriptor;
                if (descriptor == null)
                {
                    throw new InvalidOperationException();
                }
                switch (descriptor.Priority)
                {
                    case Priority.VeryLow:
                        return Resources.VeryLowPriority;
                    case Priority.Low:
                        return Resources.LowPriority;
                    case Priority.Normal:
                        return Resources.NormalPriority;
                    case Priority.High:
                        return Resources.HighPriority;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            };

            colPriority.AspectPutter += delegate
            {
                // ignored
            };
        }

        private void OnCellDictsEditStarting(object sender, CellEditEventArgs e)
        {
            EditedDictionaryDescriptor descriptor = e.RowObject as EditedDictionaryDescriptor;
            if (descriptor == null)
            {
                throw new InvalidOperationException();
            }

            if (e.Column == colDirection)
            {
                DirectionComboBox comboBox = e.Control as DirectionComboBox;
                if (comboBox == null)
                {
                    throw new InvalidOperationException();
                }

                comboBox.FillDirections(descriptor.Dictionary.Entries.From, descriptor.Dictionary.Entries.To);
                comboBox.EntryTextDirection = descriptor.EntryTextDirection;
            }
            else if (e.Column == colPriority)
            {
                PriorityComboBox comboBox = e.Control as PriorityComboBox;
                if (comboBox == null)
                {
                    throw new InvalidOperationException();
                }

                comboBox.FillPriorities(Priority.Low, Priority.Normal, Priority.High);
                comboBox.Priority = descriptor.Priority;
            }
        }

        private void OnCellDictsEditFinishing(object sender, CellEditEventArgs e)
        {
            EditedDictionaryDescriptor descriptor = e.RowObject as EditedDictionaryDescriptor;
            if (descriptor == null)
            {
                throw new InvalidOperationException();
            }

            if (e.Column == colDirection)
            {
                DirectionComboBox comboBox = e.Control as DirectionComboBox;
                if (comboBox == null)
                {
                    throw new InvalidOperationException();
                }

                if (!comboBox.OneWay)
                {
                    descriptor.EntryTextDirection = comboBox.EntryTextDirection;
                }
            }
            else if (e.Column == colPriority)
            {
                PriorityComboBox comboBox = e.Control as PriorityComboBox;
                if (comboBox == null)
                {
                    throw new InvalidOperationException();
                }

                descriptor.Priority = comboBox.Priority;
            }
        }

        private IEnumerable<EditedDictionaryDescriptor> GetEditedDicts()
        {
            foreach (EditedDictionaryDescriptor descriptor in _editedDicts)
            {
                if (descriptor.State != EditedItemState.Deleted)
                {
                    yield return descriptor;
                }
            }
        }

        private void UpdateDicts()
        {
            lvDictionaries.SetObjects(GetEditedDicts());

            UpdateDictsToolbar();
        }

        private void OnAddDictButtonClick(object sender, EventArgs e)
        {
            if (Directory.Exists(_root.MachineSettings.LastBrowseDirectory))
            {
                dlgAddDictionary.InitialDirectory = _root.MachineSettings.LastBrowseDirectory;
            }

            if (dlgAddDictionary.ShowDialog() == DialogResult.OK)
            {
                using (_root.MachineSettings.Edit())
                {
                    _root.MachineSettings.LastBrowseDirectory = Path.GetDirectoryName(dlgAddDictionary.FileName);
                }

                foreach (string path in dlgAddDictionary.FileNames)
                {
                    if (IsDictionaryActive(path))
                    {
                        MessageBox.Show(String.Format(Resources.DictionaryAlreadyUsed, dlgAddDictionary.FileName), Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                    }
                    else
                    {
                        AddExistingDictionary(path);
                    }
                }
            }
        }

        private bool IsDictionaryActive(string path)
        {
            foreach (Dictionary dict in GetActiveDicts())
            {
                if (String.Compare(dict.UserData.Path, path, true) == 0)
                {
                    return true;
                }
            }

            return false;
        }

        private Dictionary GetActiveDictionary(string path)
        {
            foreach (Dictionary dict in GetActiveDicts())
            {
                if (String.Compare(dict.UserData.Path, path, true) == 0)
                {
                    return dict;
                }
            }

            return null;
        }

        private List<Dictionary> GetActiveDicts()
        {
            List<Dictionary> result = new List<Dictionary>();

            foreach (EditedDictionaryDescriptor descriptor in _editedDicts)
            {
                if (descriptor.State != EditedItemState.Deleted)
                {
                    result.Add(descriptor.Dictionary);
                }
            }

            return result;
        }

        private void OnRemoveDictsButtonClick(object sender, EventArgs e)
        {
            RemoveSelectedDicts();
        }

        private void RemoveSelectedDicts()
        {
            foreach (EditedDictionaryDescriptor descriptor in lvDictionaries.SelectedObjects)
            {
                if (descriptor.State == EditedItemState.New)
                {
                    _root.RemoveDictionary(descriptor.Dictionary);
                    _editedDicts.Remove(descriptor);
                }
                else
                {
                    descriptor.State = EditedItemState.Deleted;
                }
            }

            lvDictionaries.RemoveObjects(lvDictionaries.SelectedObjects);
        }

        private void OnRefreshDictsButtonClick(object sender, EventArgs e)
        {
            RefreshSelectedDicts();
        }

        private void RefreshSelectedDicts()
        {
            bool failed = false;

            foreach (EditedDictionaryDescriptor descriptor in lvDictionaries.SelectedObjects)
            {
                try
                {
                    _root.RefreshDictionary(descriptor.Dictionary);
                }
                catch (DictionaryException exception)
                {
                    Logging.Error(GetType(), exception, "Dictionary could not be refreshed.");

                    failed = true;
                }
            }

            if (failed)
            {
                MessageBox.Show(Resources.DictionaryRefreshError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            lvDictionaries.RefreshObjects(lvDictionaries.SelectedObjects);
        }

        private void OnDictionaryContextMenuOpening(object sender, CancelEventArgs e)
        {
            cmiEdit.Enabled = lvDictionaries.SelectedItems.Count == 1;
            cmiRefresh.Enabled = lvDictionaries.SelectedItems.Count > 0;
            cmiRemove.Enabled = lvDictionaries.SelectedItems.Count > 0;
            cmiEnable.Enabled = lvDictionaries.SelectedItems.Count > 0;
            cmiDisable.Enabled = lvDictionaries.SelectedItems.Count > 0;
            cmiPriority.Enabled = lvDictionaries.SelectedItems.Count > 0;

            bool enabled, disabled;
            GetSelectedDictsEnabledState(out disabled, out enabled);

            if (enabled == disabled)
            {
                cmiEnable.Checked = false;
                cmiDisable.Checked = false;
            }
            else if (enabled)
            {
                cmiEnable.Checked = true;
                cmiDisable.Checked = false;
            }
            else
            {
                cmiEnable.Checked = false;
                cmiDisable.Checked = true;
            }
        }

        private void OnContextMenuDictsRefreshClick(object sender, EventArgs e)
        {
            RefreshSelectedDicts();
        }

        private void OnContextMenuDictsRemoveClick(object sender, EventArgs e)
        {
            RemoveSelectedDicts();
        }

        private void OnSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            UpdateDictsToolbar();
        }

        private void UpdateDictsToolbar()
        {
            tsbRemoveDict.Enabled = lvDictionaries.SelectedItems.Count > 0;
            tsbRefreshDict.Enabled = lvDictionaries.SelectedItems.Count > 0;
            tsbEditDict.Enabled = lvDictionaries.SelectedItems.Count > 0;
            tsbEnabled.Enabled = lvDictionaries.SelectedItems.Count > 0;
            tsbPriority.Enabled = lvDictionaries.SelectedItems.Count > 0;
        }

        private void OnCreateDictButtonClick(object sender, EventArgs e)
        {
            EntriesEditorForm entriesEditorForm = new EntriesEditorForm(_root, GetActiveDicts());

            entriesEditorForm.PseudoParent = this;
            entriesEditorForm.Show();
            entriesEditorForm.Closed += delegate
            {
                if (entriesEditorForm.CreatedDictionary != null)
                {
                    _editedDicts.Add(EditedDictionaryDescriptor.CreateNew(entriesEditorForm.CreatedDictionary));
                }

                UpdateDicts();
            };
        }

        private void OnEditSelectedDict(object sender, EventArgs e)
        {
            EditSelectedDict();
        }

        private void OnContextMenuEditEntries(object sender, EventArgs e)
        {
            EditSelectedDict();
        }

        private void EditSelectedDict()
        {
            EditedDictionaryDescriptor descriptor = lvDictionaries.SelectedObject as EditedDictionaryDescriptor;
            if (descriptor == null)
            {
                throw new InvalidOperationException();
            }

            EntriesEditorForm form = new EntriesEditorForm(_root, GetActiveDicts(), descriptor.Dictionary);
            _root.FormsManager.Add(form);
            form.PseudoParent = this;
            form.Show();
            form.Closed += delegate
            {
                try
                {
                    UpdateDicts();
                }
                finally
                {
                    _root.FormsManager.Remove(form);
                }
            };
        }

        private void OnDictsDoubleClick(object sender, EventArgs e)
        {
            if (lvDictionaries.SelectedObjects.Count == 1)
            {
                EditSelectedDict();
            }
        }

        private void RevertDicts()
        {
            foreach (EditedDictionaryDescriptor descriptor in _editedDicts)
            {
                if (descriptor.State == EditedItemState.New)
                {
                    _root.RemoveDictionary(descriptor.Dictionary);
                }
            }
        }

        private bool AreDictsModified()
        {
            foreach (EditedDictionaryDescriptor descriptor in _editedDicts)
            {
                switch (descriptor.State)
                {
                    case EditedItemState.Normal:
                        if (descriptor.IsModified())
                        {
                            return true;
                        }
                        break;
                    case EditedItemState.Deleted:
                        return true;
                    case EditedItemState.New:
                        return true;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return false;
        }

        private void AddExistingDictionary(string path)
        {
            Dictionary dictionary;

            try
            {
                dictionary = _root.AddNewDictionary(path);
            }
            catch (DictionaryException exception)
            {
                Logging.Error(GetType(), exception, "Dictionary could not be added.");
                MessageBox.Show(Resources.DictionaryCreateError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                return;
            }

            if (dictionary == null)
            {
                return;
            }

            EditedDictionaryDescriptor descriptor = EditedDictionaryDescriptor.CreateNew(dictionary);

            lvDictionaries.AddObject(descriptor);
            _editedDicts.Add(descriptor);
        }

        void OnRefreshDictionaryUI(object sender, InputEventArgs<Dictionary> e)
        {
            foreach (Dictionary dictionary in GetActiveDicts())
            {
                if (dictionary == e.Input)
                {
                    lvDictionaries.RefreshObject(dictionary);
                    break;
                }
            }
        }

        void OnAddDictionaryUI(object sender, InputEventArgs<Dictionary> e)
        {
            EditedDictionaryDescriptor descriptor = EditedDictionaryDescriptor.CreateNew(e.Input);
            lvDictionaries.AddObject(descriptor);
            _editedDicts.Add(descriptor);
        }

        void OnIsDictionaryLoaded(object sender, InputOutputEventArgs<string, bool> e)
        {
            e.Output = GetActiveDictionary(e.Input) != null;
        }

        void OnGetLoadedDictionaryId(object sender, InputOutputEventArgs<string, Guid> e)
        {
            e.Output = GetActiveDictionary(e.Input) != null ? GetActiveDictionary(e.Input).Entries.Id : Guid.Empty;
        }

        void OnGetLoadedDictionary(object sender, InputOutputEventArgs<string, Dictionary> e)
        {
            e.Output = GetActiveDictionary(e.Input);
        }

        private void OnAddOnlineDictsButtonClick(object sender, EventArgs e)
        {
            OnlineDictsManager manager = new OnlineDictsManager(_root, this);

            manager.GetLoadedDictionary += OnGetLoadedDictionary;
            manager.GetLoadedDictionaryId += OnGetLoadedDictionaryId;
            manager.IsDictionaryLoaded += OnIsDictionaryLoaded;
            manager.AddDictionaryUI += OnAddDictionaryUI;
            manager.RefreshDictionaryUI += OnRefreshDictionaryUI;

            manager.AddOnlineDicts();
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (DialogResult == DialogResult.Cancel)
            {
                if (AreDictsModified())
                {
                    RevertDicts();
                }

                _root.UserSettings.Revert();

                return;
            }

            if (AreDictsModified())
            {
                SaveDicts();
            }

            _root.UserSettings.CheckIn();
        }

        private void InitializePriorityChanger()
        {
            cmiPriority.Text = colPriority.Text;

            cmiPriority.DropDownItems.Add(Resources.LowPriority, Resources.priority_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Low));
            cmiPriority.DropDownItems.Add(Resources.NormalPriority, Resources.priority_normal).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Normal));
            cmiPriority.DropDownItems.Add(Resources.HighPriority, Resources.priority_high).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.High));

            tsbPriority.DropDownItems.Add(Resources.LowPriority, Resources.priority_low).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Low));
            tsbPriority.DropDownItems.Add(Resources.NormalPriority, Resources.priority_normal).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.Normal));
            tsbPriority.DropDownItems.Add(Resources.HighPriority, Resources.priority_high).Click += ((sender, e) => ChangePriorityForSelectedItems(Priority.High));
        }

        private delegate void ModifyDictionaryDelegate(EditedDictionaryDescriptor descriptor);

        private void ModifySelectedDicts(ModifyDictionaryDelegate modifyDictionaryDelegate)
        {
            foreach (EditedDictionaryDescriptor descriptor in lvDictionaries.SelectedObjects)
            {
                modifyDictionaryDelegate(descriptor);
            }

            lvDictionaries.RefreshObjects(lvDictionaries.SelectedObjects);

            UpdateDictsToolbar();
        }

        private void ChangePriorityForSelectedItems(Priority priority)
        {
            ModifySelectedDicts(delegate(EditedDictionaryDescriptor descriptor)
            {
                descriptor.Priority = priority;
            });
        }

        private void OnEnableSelection(object sender, EventArgs e)
        {
            ModifySelectedDicts(delegate(EditedDictionaryDescriptor descriptor)
            {
                descriptor.Enabled = true;
            });
        }

        private void OnDisableSelection(object sender, EventArgs e)
        {
            ModifySelectedDicts(delegate(EditedDictionaryDescriptor descriptor)
            {
                descriptor.Enabled = false;
            });
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            _root.MachineSettings.CheckIn();
        }

        private void OnEnabledDisabledMenuOpening(object sender, EventArgs e)
        {
            bool disabled, enabled;

            GetSelectedDictsEnabledState(out disabled, out enabled);

            if (enabled == disabled)
            {
                miEnable.Checked = false;
                miDisable.Checked = false;
            }
            else if (enabled)
            {
                miEnable.Checked = true;
                miDisable.Checked = false;
            }
            else
            {
                miEnable.Checked = false;
                miDisable.Checked = true;
            }
        }

        private void GetSelectedDictsEnabledState(out bool disabled, out bool enabled)
        {
            List<EditedDictionaryDescriptor> selectedActiveDicts = GetSelectedActiveDicts();

            enabled = false;
            disabled = false;

            foreach (EditedDictionaryDescriptor dictionary in selectedActiveDicts)
            {
                if (dictionary.Enabled)
                {
                    enabled = true;
                }
                else
                {
                    disabled = true;
                }

                if (enabled && disabled)
                {
                    break;
                }
            }
        }

        private List<EditedDictionaryDescriptor> GetSelectedActiveDicts()
        {
            List<EditedDictionaryDescriptor> selectedActiveDicts = new List<EditedDictionaryDescriptor>();

            foreach (EditedDictionaryDescriptor descriptor in lvDictionaries.SelectedObjects)
            {
                if (descriptor.State != EditedItemState.Deleted)
                {
                    selectedActiveDicts.Add(descriptor);
                }
            }
            return selectedActiveDicts;
        }
    }
}
